using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using XNAProject.Core;


namespace XNAProject.GameElements
{
    public class Hexagon : Microsoft.Xna.Framework.DrawableGameComponent
    {                                                                                           //just a scratch
        #region Fields

        public Vector2 position { get; set; }                                                   //props

        #region Graphics

        Model model;
        Model oilSpillModel;
        Model mistModel;

        Effect effect;
        Effect oilSpillEffect;
        Effect mistEffect;

        Texture2D colorMap;
        Texture2D selectedColorMap;
        Texture2D notSelectedColorMap;
        Texture2D highlightedColorMap;
        Texture2D attackedColorMap;
        Texture2D wayColorMap;
        Texture2D oilSpillColorMap;
        Texture2D oilSpillNormalMap;   
        Texture2D mistColorMap;
        Texture2D mistNormalMap;

        Matrix[] modelBones, oilSpillBones, mistBones;

        public float Scale { get; set; }
        public Vector3 Rotation { get; set; }
        public Vector3 Translation { get; set; }
        public float Hue { get; set; }
        public float oilSpillHue { get; set; }
        public float mistHue { get; set; }
        public Vector4 vLightDirection { get; set; }
        public float Width { get; set; }
        public float Height { get; set; }

        #endregion

        public bool attacked { get; set; }
        public bool moved { get; set; } 
        public bool selected { get; set; }
        public bool way { get; set; }
        public bool highlighted { get; set; }
        public bool visible { get; set; }
        public int oil { get; set; }
        private Ship _ship;
        public Ship ship {
            get
            {
                return _ship;
            }
            
            set
            {
                if (value != null) shipOn = true;
                else shipOn = false;
                _ship = value;
            }
        }
        public bool enabled { get; set; }
        public bool shipOn { get; set; }
        public bool discovered { get; set; }
        public bool oilSpill { get; set; }

        public int defenseBonus { get; set; }
        public int offenseBonus { get; set; }

        public string name { get; set; }

        private Level thisLevel;

        #endregion

        #region BasicProcedures

        public Hexagon(Game game, Vector2 Position, Vector2 hexaPosition, bool Enabled, bool Visible, int DefenseBonus, int OffenseBonus, int Oil)            
            : base(game)
        {
            position = Position;                                                                //constructor
            defenseBonus = DefenseBonus;
            offenseBonus = OffenseBonus;
            selected = false;
            oilSpill = false;
            way = false;
            discovered = false;
            visible = Visible;
            oil = Oil;
            enabled = Enabled;

            name = "Ocean";
            Scale = 22.0f;
            Hue = 1.0f;
            oilSpillHue = 2.0f;
            mistHue = 2.0f;
            Rotation = new Vector3(0, 0, 0);
            Width = (float)7.8 * Scale;
            Height = (float)9 * Scale;
            Translation = new Vector3(position.X * Width + hexaPosition.X,
                -30, ((position.X % 2) * (Height / 2)) + position.Y * Height + hexaPosition.Y);
            vLightDirection = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);

            thisLevel = (Level)Game.Services.GetService(typeof(Level));
        }

        public override void Initialize()                                                       //init
        {
            //effect = Game.Content.Load<Effect>("Effects/HexagonShader");                        
            effect = new BasicEffect(Game.GraphicsDevice);
            oilSpillEffect = Game.Content.Load<Effect>("Effects/OilSpillShader");
            mistEffect = Game.Content.Load<Effect>("Effects/mistShader");

            selectedColorMap = Game.Content.Load<Texture2D>("Textures/Level/selectedHexagon");
            notSelectedColorMap = Game.Content.Load<Texture2D>("Textures/Level/notSelectedHexagon");
            highlightedColorMap = Game.Content.Load<Texture2D>("Textures/level/highlightedHexagon");
            attackedColorMap = Game.Content.Load<Texture2D>("Textures/level/attackedHexagon");
            wayColorMap = Game.Content.Load<Texture2D>("Textures/level/moveHexagon");

            oilSpillColorMap = Game.Content.Load<Texture2D>("Textures/Level/oilSpillColorMap");
            oilSpillNormalMap = Game.Content.Load<Texture2D>("Textures/Level/wavesbump");

            mistColorMap = Game.Content.Load<Texture2D>("Textures/Level/mist");
            mistNormalMap = Game.Content.Load<Texture2D>("Textures/Level/wavesbump");

            colorMap = notSelectedColorMap;

            base.Initialize();
        }

        protected override void  LoadContent()                                                  //load models
        {
            model = Game.Content.Load<Model>("Models/Level/hexagon");          
            modelBones = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(modelBones);
         
            oilSpillModel = Game.Content.Load<Model>("Models/Level/oilSpill");  
            oilSpillBones = new Matrix[oilSpillModel.Bones.Count];
            oilSpillModel.CopyAbsoluteBoneTransformsTo(oilSpillBones);

            mistModel = Game.Content.Load<Model>("Models/Level/mist");  
            mistBones = new Matrix[mistModel.Bones.Count];
            mistModel.CopyAbsoluteBoneTransformsTo(mistBones);

 	        base.LoadContent();
        }

        public override void Update(GameTime gameTime)                                          //update
        {                                                                           
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)                                            //draw
        {
            XNAProjectCamera camera = (XNAProjectCamera)Game.Services.GetService(typeof(XNAProjectCamera));

            #region Hexagon

            //effect.CurrentTechnique = effect.Techniques["NormalMapping"];

            camera.renderMatrix = Matrix.CreateScale(Scale) * Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y)
                * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateTranslation(Translation);

            Game.GraphicsDevice.BlendState = BlendState.AlphaBlend;
            //Game.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
            //Game.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

            //foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            //{
            
                //foreach (ModelMeshPart part in mesh.MeshParts)
                //{
            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;

                    effect.World = camera.renderMatrix;

                    effect.Projection = camera.Projection;
                    effect.View = camera.View;
                    
                    

                    //camera.worldMatrix = modelBones[mesh.ParentBone.Index] * camera.renderMatrix;

                    //Vector4 vecEye = new Vector4(camera.Position.X, camera.Position.Y, camera.Position.Z, 0);

                    //Matrix worldInverse = Matrix.Invert(camera.worldMatrix);

                    //effect.Parameters["matWorldViewProj"].SetValue(camera.worldMatrix * camera.View * camera.Projection);
                    //effect.Parameters["matWorld"].SetValue(camera.worldMatrix);
                    //effect.Parameters["vecEye"].SetValue(vecEye);
                    //effect.Parameters["vecLightDir"].SetValue(vLightDirection);
                    //effect.Parameters["ColorMap"].SetValue(colorMap);
                    //effect.Parameters["A"].SetValue(Hue);


                    //VertexBuffer vb = new VertexBuffer(Game.GraphicsDevice, typeof(VertexPositionNormalTexture),
                    //                                    part.VertexBuffer.VertexCount, BufferUsage.None);

                    //byte[] shit = new byte[2]; 
                    //vb.SetData<VertexDeclaration>(part.VertexBuffer.VertexDeclaration);
                    //vb.SetData<byte>(part.VertexOffset, null, part.NumVertices, part.PrimitiveCount, 2);

                    //Game.GraphicsDevice.SetVertexBuffer(vb);

                    //Game.GraphicsDevice.SetVertexBuffer(part.VertexBuffer, part.VertexOffset);

                    //Game.GraphicsDevice.Indices = part.IndexBuffer;

                    //pass.Apply();
                    //Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                    //    part.NumVertices, part.StartIndex, part.PrimitiveCount);
                }
                mesh.Draw();
            }
            //}

            #endregion                                                                                 //draw the hexagon itself

            if (visible)
            {
                /*
                #region oilSpill

                if (oilSpill)                                                                   //oil spill draw
                {
                    {
                        oilSpillEffect.CurrentTechnique = oilSpillEffect.Techniques["OceanEffect"];

                        Matrix renderMatrix = Matrix.CreateScale(Scale - 10) * Matrix.CreateRotationX(Rotation.X ) * Matrix.CreateRotationY(Rotation.Y)
                            * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateTranslation(Translation.X, Translation.Y + 0, Translation.Z);

                        foreach (EffectPass pass in oilSpillEffect.CurrentTechnique.Passes)
                        {
                            foreach (ModelMesh mesh in oilSpillModel.Meshes)
                            {
                                foreach (ModelMeshPart part in mesh.MeshParts)
                                {
                                    camera.worldMatrix = oilSpillBones[mesh.ParentBone.Index] * renderMatrix;

                                    Vector4 vecEye = new Vector4(camera.Position.X, camera.Position.Y, camera.Position.Z, 0);

                                    Matrix worldInverse = Matrix.Invert(camera.worldMatrix);

                                    oilSpillEffect.Parameters["matWorldViewProj"].SetValue(camera.worldMatrix * camera.View * camera.Projection);
                                    oilSpillEffect.Parameters["matWorld"].SetValue(camera.worldMatrix);
                                    oilSpillEffect.Parameters["vecEye"].SetValue(vecEye);
                                    oilSpillEffect.Parameters["vecLightDir"].SetValue(vLightDirection);
                                    oilSpillEffect.Parameters["ColorMap"].SetValue(oilSpillColorMap);
                                    oilSpillEffect.Parameters["BumpMap"].SetValue(oilSpillNormalMap);
                                    oilSpillEffect.Parameters["time"].SetValue(camera.moveObject / 2);
                                    oilSpillEffect.Parameters["A"].SetValue(oilSpillHue);
                                    oilSpillEffect.Parameters["bSpecular"].SetValue(true);

                                    pass.Apply();

                                    //GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
                                    Game.GraphicsDevice.Indices = part.IndexBuffer;
                                    Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                                        part.NumVertices, part.StartIndex, part.PrimitiveCount);
                                }
                            }
                        }
                    }
                }

                #endregion
                */
            }
            else
            {/*
                #region Mist
                                                                                                //mist draw
                mistEffect.CurrentTechnique = mistEffect.Techniques["NormalMapping"];

                camera.renderMatrix = Matrix.CreateScale(Scale - 10) * Matrix.CreateRotationX(Rotation.X) * Matrix.CreateRotationY(Rotation.Y)
                    * Matrix.CreateRotationZ(Rotation.Z) * Matrix.CreateTranslation(Translation.X, Translation.Y - 1, Translation.Z);

                Game.GraphicsDevice.BlendState = BlendState.AlphaBlend;
                //Game.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
                //Game.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;

                //foreach (EffectPass pass in mistEffect.CurrentTechnique.Passes)
                //{

                    foreach (ModelMesh mesh in mistModel.Meshes)
                    {
                        //foreach (ModelMeshPart part in mesh.MeshParts)
                        //{
                        foreach (BasicEffect effect in mesh.Effects)
                        {
                            effect.EnableDefaultLighting();
                            effect.PreferPerPixelLighting = true;

                            effect.World =
                                Matrix.CreateFromYawPitchRoll(Rotation.Y,Rotation.X,Rotation.Z) *
                                Matrix.CreateScale(Scale) *
                                Matrix.CreateTranslation(Translation);

                            effect.Projection = camera.Projection;
                            effect.View = camera.View;

                            //camera.worldMatrix = mistBones[mesh.ParentBone.Index] * camera.renderMatrix;

                            //Vector4 vecEye = new Vector4(camera.Position.X, camera.Position.Y, camera.Position.Z, 0);

                            //Matrix worldInverse = Matrix.Invert(camera.worldMatrix);

                            //mistEffect.Parameters["matWorldViewProj"].SetValue(camera.worldMatrix * camera.View * camera.Projection);
                            //mistEffect.Parameters["matWorld"].SetValue(camera.worldMatrix);
                            //mistEffect.Parameters["vecEye"].SetValue(vecEye);
                            //mistEffect.Parameters["vecLightDir"].SetValue(vLightDirection);
                            //mistEffect.Parameters["ColorMap"].SetValue(mistColorMap);
                            //mistEffect.Parameters["NormalMap"].SetValue(mistNormalMap);
                            //mistEffect.Parameters["A"].SetValue(mistHue);

                            //pass.Apply();

                            ////GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, part.StreamOffset, part.VertexStride);
                            //Game.GraphicsDevice.Indices = part.IndexBuffer;
                            //Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                            //    part.NumVertices, part.StartIndex, part.PrimitiveCount);
                        }
                        mesh.Draw();
                    }
                //}

                Game.GraphicsDevice.BlendState = BlendState.NonPremultiplied;

                #endregion*/
            }

            base.Draw(gameTime);
        }

        public bool Intersect(Ray ray)                                                          //basic collosion detection
        {                                                                                       
            //Matrix transform = Matrix.CreateTranslation(Translation);            

            //Matrix inverseTransform = Matrix.Invert(transform);

            //ray.Position = Vector3.Transform(ray.Position, inverseTransform);
            //ray.Direction = Vector3.TransformNormal(ray.Direction, inverseTransform);

            BoundingSphere boundingSphere = model.Meshes[0].BoundingSphere;
            boundingSphere.Radius = (float)(Width / 2);
            boundingSphere.Center = Translation;

            if (boundingSphere.Intersects(ray) == null)            
                return false;            
            else return true;             
        }

        public List<Vector2> GetAreaAroundSelf(int areaRadius)
        //give back List<Vector2> so we can manipulate further if needed
        {
            //declare list to hold positions
            List<Vector2> areaAround = new List<Vector2>();
            //math to determine neighbours
            for (int i = 0; i < areaRadius + 1; i++)
            {
                for (int j = 0; j < 2 * areaRadius - i + 1; j++)
                {
                    Vector2 tempRight, tempLeft;

                    tempRight = new Vector2((int)position.X + i, (int)position.Y - j - i + (i / 2) + areaRadius);

                    if (position.X % 2 == 1)
                    {
                        tempRight.Y += i % 2;
                    }
                    tempLeft = new Vector2((int)position.X - i, tempRight.Y);
                    //2 neighbours are calculated at this point tempLeft, tempRight

                    //if it isnt already in there
                    if (tempRight.X < thisLevel.size.X && tempRight.X >= 0 &&
                            tempRight.Y >= 0 && tempRight.Y < thisLevel.size.Y) //bounds
                        if (!areaAround.Contains(tempRight))
                        {
                            areaAround.Add(tempRight);  //add to list
                        }

                    if (tempLeft.X < thisLevel.size.X && tempLeft.X >= 0 &&
                        tempLeft.Y >= 0 && tempLeft.Y < thisLevel.size.Y) //bounds
                        if (!areaAround.Contains(tempLeft))
                        {
                            areaAround.Add(tempLeft);   //add to list
                        }
                }
            }
            return areaAround;
        }

        public void Select()                                                                    //select / unselect
        {
            selected = true;
            colorMap = selectedColorMap;
            if (ship != null)
                ship.Select();
        }

        public void UnSelect()
        {
            selected = false;
            colorMap = notSelectedColorMap;
            if (ship != null)
                ship.UnSelect();
        }

        public void UnTarget()
        {
            attacked = false;
            moved = false;
            if (!selected && !way)
                colorMap = notSelectedColorMap;
            else colorMap = selectedColorMap;
        }

        public void Highlight()
        {
            if (!selected && !attacked && !moved && !way)
                colorMap = highlightedColorMap; 
        }

        public void UnHighlight()
        {
            if (!selected && !attacked && !moved && !way)
                colorMap = notSelectedColorMap;
        }

        public void AttackTarget()
        {
            attacked = true;
            colorMap = attackedColorMap; 
        }

        public void Waypoint()
        {
            way = true;
            colorMap = wayColorMap;
        }

        public void EndWaypoint()
        {
            way = false;
            colorMap = notSelectedColorMap;
        }

        #endregion

        public bool InMe(Vector3 pickedPosition)
        {
            float x = pickedPosition.X;
            float y = pickedPosition.Z;
            Game.Window.Title = x.ToString() + ", " + y.ToString();
            return true;
        }
    }
}